home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr49 / pgp23src.zip / MD5SUM.C < prev    next >
C/C++ Source or Header  |  1993-06-15  |  5KB  |  244 lines

  1. /*
  2.  * md5sum.c    - Generate/check MD5 Message Digests
  3.  *
  4.  * Compile and link with md5.c.  If you don't have getopt() in your library
  5.  * also include getopt.c.  For MSDOS you can also link with the wildcard
  6.  * initialization function (wildargs.obj for Turbo C and setargv.obj for MSC)
  7.  * so that you can use wildcards on the commandline.
  8.  *
  9.  * Written March 1993 by Branko Lankester
  10.  * Modified June 1993 by Colin Plumb for altered md5.c.
  11.  */
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include "md5.h"
  15.  
  16. #ifdef UNIX
  17. #define    FOPRTXT    "r"
  18. #define    FOPRBIN    "r"
  19. #else
  20. #ifdef VMS
  21. #define    FOPRTXT    "r","ctx=stm"
  22. #define    FOPRBIN    "rb","ctx=stm"
  23. #else
  24. #define    FOPRTXT    "r"
  25. #define    FOPRBIN    "rb"
  26. #endif
  27. #endif
  28.  
  29. extern char *optarg;
  30. extern int optind;
  31.  
  32. void usage();
  33. void print_digest();
  34. int mdfile(FILE *fp, unsigned char *digest);
  35. int do_check(FILE *chkf);
  36.  
  37. char *progname;
  38. int verbose = 0;
  39. int bin_mode = 0;
  40.  
  41. void
  42. main(int argc, char **argv)
  43. {
  44.     int opt, rc = 0;
  45.     int check = 0;
  46.     FILE *fp;
  47.     unsigned char digest[16];
  48.  
  49.     progname = *argv;
  50.     while ((opt = getopt(argc, argv, "cbvp:h")) != EOF) {
  51.         switch (opt) {
  52.             case 'c': check = 1; break;
  53.             case 'v': verbose = 1; break;
  54.             case 'b': bin_mode = 1; break;
  55.             default: usage();
  56.         }
  57.     }
  58.     argc -= optind;
  59.     argv += optind;
  60.     if (check) {
  61.         switch (argc) {
  62.             case 0: fp = stdin; break;
  63.             case 1: if ((fp = fopen(*argv, FOPRTXT)) == NULL) {
  64.                     perror(*argv);
  65.                     exit(2);
  66.                 }
  67.                 break;
  68.             default: usage();
  69.         }
  70.         exit(do_check(fp));
  71.     }
  72.     if (argc == 0) {
  73.         if (mdfile(stdin, digest)) {
  74.             fprintf(stderr, "%s: read error on stdin\n", progname);
  75.             exit(2);
  76.         }
  77.         print_digest(digest);
  78.         printf("\n");
  79.         exit(0);
  80.     }
  81.     for ( ; argc > 0; --argc, ++argv) {
  82.         if (bin_mode)
  83.             fp = fopen(*argv, FOPRBIN);
  84.         else
  85.             fp = fopen(*argv, FOPRTXT);
  86.         if (fp == NULL) {
  87.             perror(*argv);
  88.             rc = 2;
  89.             continue;
  90.         }
  91.         if (mdfile(fp, digest)) {
  92.             fprintf(stderr, "%s: error reading %s\n", progname, *argv);
  93.             rc = 2;
  94.         } else {
  95.             print_digest(digest);
  96.             printf(" %c%s\n", bin_mode ? '*' : ' ', *argv);
  97.         }
  98.         fclose(fp);
  99.     }
  100.     exit(rc);
  101. }
  102.  
  103. void
  104. usage()
  105. {
  106.     fprintf(stderr, "usage: md5sum [-bv] [-c [file]] | [file...]\n");
  107.     fprintf(stderr, "Generates or checks MD5 Message Digests\n");
  108.     fprintf(stderr, "    -c  check message digests (default is generate)\n");
  109.     fprintf(stderr, "    -v  verbose, print file names when checking\n");
  110.     fprintf(stderr, "    -b  read files in binary mode\n");
  111.     fprintf(stderr, "The input for -c should be the list of message digests and file names\n");
  112.     fprintf(stderr, "that is printed on stdout by this program when it generates digests.\n");
  113.     exit(2);
  114. }
  115.  
  116. int
  117. mdfile(FILE *fp, unsigned char *digest)
  118. {
  119.     unsigned char buf[1024];
  120.     MD5_CTX ctx;
  121.     int n;
  122.  
  123.     MD5Init(&ctx);
  124.     while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
  125.         MD5Update(&ctx, buf, n);
  126.     MD5Final(digest, &ctx);
  127.     if (ferror(fp))
  128.         return -1;
  129.     return 0;
  130. }
  131.  
  132. void
  133. print_digest(unsigned char *p)
  134. {
  135.     int i;
  136.  
  137.     for (i = 0; i < 16; ++i)
  138.         printf("%02x", *p++);
  139. }
  140.  
  141. int
  142. hex_digit(int c)
  143. {
  144.     if (c >= '0' && c <= '9')
  145.         return c - '0';
  146.     if (c >= 'a' && c <= 'f')
  147.         return c - 'a' + 10;
  148.     return -1;
  149. }
  150.  
  151. int
  152. get_md5_line(FILE *fp, unsigned char *digest, char *file)
  153. {
  154.     char buf[1024];
  155.     int i, d1, d2, rc;
  156.     char *p = buf;
  157.  
  158.     if (fgets(buf, sizeof(buf), fp) == NULL)
  159.         return -1;
  160.  
  161.     for (i = 0; i < 16; ++i) {
  162.         if ((d1 = hex_digit(*p++)) == -1)
  163.             return 0;
  164.         if ((d2 = hex_digit(*p++)) == -1)
  165.             return 0;
  166.         *digest++ = d1*16 + d2;
  167.     }
  168.     if (*p++ != ' ')
  169.         return 0;
  170.     /*
  171.      * next char is an attribute char, space means text file
  172.      * if it's a '*' the file should be checked in binary mode.
  173.      */
  174.     if (*p == ' ')
  175.         rc = 1;
  176.     else if (*p == '*')
  177.         rc = 2;
  178.     else {
  179.         fprintf(stderr, "%s: unrecognized line: %s", progname, buf);
  180.         return 0;
  181.     }
  182.     ++p;
  183.     i = strlen(p);
  184.     if (i < 2 || i > 255)
  185.         return 0;
  186.     p[i-1] = '\0';
  187.     strcpy(file, p);
  188.     return rc;
  189. }
  190.  
  191. int
  192. do_check(FILE *chkf)
  193. {
  194.     int rc, ex = 0, failed = 0, checked = 0;
  195.     unsigned char chk_digest[16], file_digest[16];
  196.     char filename[256];
  197.     FILE *fp;
  198.     int flen = 14;
  199.  
  200.     while ((rc = get_md5_line(chkf, chk_digest, filename)) >= 0) {
  201.         if (rc == 0)    /* not an md5 line */
  202.             continue;
  203.         if (verbose) {
  204.             if (strlen(filename) > flen)
  205.                 flen = strlen(filename);
  206.             fprintf(stderr, "%-*s ", flen, filename);
  207.         }
  208.         if (bin_mode || rc == 2)
  209.             fp = fopen(filename, FOPRBIN);
  210.         else
  211.             fp = fopen(filename, FOPRTXT);
  212.         if (fp == NULL) {
  213.             fprintf(stderr, "%s: can't open %s\n", progname, filename);
  214.             ex = 2;
  215.             continue;
  216.         }
  217.         if (mdfile(fp, file_digest)) {
  218.             fprintf(stderr, "%s: error reading %s\n", progname, filename);
  219.             ex = 2;
  220.             fclose(fp);
  221.             continue;
  222.         }
  223.         fclose(fp);
  224.         if (memcmp(chk_digest, file_digest, 16) != 0) {
  225.             if (verbose)
  226.                 fprintf(stderr, "FAILED\n");
  227.             else
  228.                 fprintf(stderr, "%s: MD5 check failed for '%s'\n", progname, filename);
  229.             ++failed;
  230.         } else if (verbose)
  231.             fprintf(stderr, "OK\n");
  232.         ++checked;
  233.     }
  234.     if (verbose && failed)
  235.         fprintf(stderr, "%s: %d of %d file(s) failed MD5 check\n", progname, failed, checked);
  236.     if (!checked) {
  237.         fprintf(stderr, "%s: no files checked\n", progname);
  238.         return 3;
  239.     }
  240.     if (!ex && failed)
  241.         ex = 1;
  242.     return ex;
  243. }
  244.